home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 4: GNU Archives / Linux Cubed Series 4 - GNU Archives.iso / gnu / gawk-3.000 / gawk-3 / gawk-3.0.0 / main.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-01-11  |  21.5 KB  |  845 lines

  1. /*
  2.  * main.c -- Expression tree constructors and main program for gawk. 
  3.  */
  4.  
  5. /* 
  6.  * Copyright (C) 1986, 1988, 1989, 1991-1995 the Free Software Foundation, Inc.
  7.  * 
  8.  * This file is part of GAWK, the GNU implementation of the
  9.  * AWK Programming Language.
  10.  * 
  11.  * GAWK is free software; you can redistribute it and/or modify
  12.  * it under the terms of the GNU General Public License as published by
  13.  * the Free Software Foundation; either version 2 of the License, or
  14.  * (at your option) any later version.
  15.  * 
  16.  * GAWK is distributed in the hope that it will be useful,
  17.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19.  * GNU General Public License for more details.
  20.  * 
  21.  * You should have received a copy of the GNU General Public License
  22.  * along with this program; if not, write to the Free Software
  23.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
  24.  */
  25.  
  26. #include "awk.h"
  27. #include "getopt.h"
  28. #include "patchlevel.h"
  29.  
  30. static void usage P((int exitval));
  31. static void copyleft P((void));
  32. static void cmdline_fs P((char *str));
  33. static void init_args P((int argc0, int argc, char *argv0, char **argv));
  34. static void init_vars P((void));
  35. static void pre_assign P((char *v));
  36. RETSIGTYPE catchsig P((int sig, int code));
  37. static void gawk_option P((char *optstr));
  38. static void nostalgia P((void));
  39. static void version P((void));
  40.  
  41. /* These nodes store all the special variables AWK uses */
  42. NODE *ARGC_node, *ARGIND_node, *ARGV_node, *CONVFMT_node, *ENVIRON_node;
  43. NODE *ERRNO_node, *FIELDWIDTHS_node, *FILENAME_node, *FNR_node, *FS_node;
  44. NODE *IGNORECASE_node, *NF_node, *NR_node, *OFMT_node, *OFS_node;
  45. NODE *ORS_node, *RLENGTH_node, *RSTART_node, *RS_node, *RT_node, *SUBSEP_node;
  46.  
  47. long NF;
  48. long NR;
  49. long FNR;
  50. int IGNORECASE;
  51. char *OFS;
  52. char *ORS;
  53. char *OFMT;
  54.  
  55. /*
  56.  * CONVFMT is a convenience pointer for the current number to string format.
  57.  * We must supply an initial value to avoid recursion problems of
  58.  *    set_CONVFMT -> fmt_index -> r_force_string: gets NULL CONVFMT
  59.  * Fun, fun, fun, fun.
  60.  */
  61. char *CONVFMT = "%.6g";
  62.  
  63. int errcount = 0;        /* error counter, used by yyerror() */
  64.  
  65. NODE *Nnull_string;        /* The global null string */
  66.  
  67. /* The name the program was invoked under, for error messages */
  68. const char *myname;
  69.  
  70. /* A block of AWK code to be run before running the program */
  71. NODE *begin_block = NULL;
  72.  
  73. /* A block of AWK code to be run after the last input file */
  74. NODE *end_block = NULL;
  75.  
  76. int exiting = FALSE;        /* Was an "exit" statement executed? */
  77. int exit_val = 0;        /* optional exit value */
  78.  
  79. #if defined(YYDEBUG) || defined(DEBUG)
  80. extern int yydebug;
  81. #endif
  82.  
  83. struct src *srcfiles = NULL;    /* source file name(s) */
  84. long numfiles = -1;        /* how many source files */
  85.  
  86. int do_traditional = FALSE;    /* no gnu extensions, add traditional weirdnesses */
  87. int do_posix = FALSE;        /* turn off gnu and unix extensions */
  88. int do_lint = FALSE;        /* provide warnings about questionable stuff */
  89. int do_lint_old = FALSE;    /* warn about stuff not in V7 awk */
  90. int do_nostalgia = FALSE;    /* provide a blast from the past */
  91. int do_intervals = FALSE;    /* allow {...,...} in regexps */
  92.  
  93. int in_begin_rule = FALSE;    /* we're in a BEGIN rule */
  94. int in_end_rule = FALSE;    /* we're in a END rule */
  95.  
  96. int output_is_tty = FALSE;    /* control flushing of output */
  97.  
  98. extern char *version_string;    /* current version, for printing */
  99.  
  100. /* The parse tree is stored here.  */
  101. NODE *expression_value;
  102.  
  103. static struct option optab[] = {
  104.     { "compat",        no_argument,        & do_traditional,    1 },
  105.     { "traditional",    no_argument,        & do_traditional,    1 },
  106.     { "lint",        no_argument,        & do_lint,    1 },
  107.     { "lint-old",        no_argument,        & do_lint_old,    1 },
  108.     { "posix",        no_argument,        & do_posix,    1 },
  109.     { "nostalgia",        no_argument,        & do_nostalgia,    1 },
  110.     { "copyleft",        no_argument,        NULL,        'C' },
  111.     { "copyright",        no_argument,        NULL,        'C' },
  112.     { "field-separator",    required_argument,    NULL,        'F' },
  113.     { "file",        required_argument,    NULL,        'f' },
  114.     { "re-interval",        no_argument,    & do_intervals,        1 },
  115.     { "source",        required_argument,    NULL,        's' },
  116.     { "assign",        required_argument,    NULL,        'v' },
  117.     { "version",        no_argument,        NULL,        'V' },
  118.     { "usage",        no_argument,        NULL,        'u' },
  119.     { "help",        no_argument,        NULL,        'u' },
  120. #ifdef DEBUG
  121.     { "parsedebug",        no_argument,        NULL,        'D' },
  122. #endif
  123.     { NULL, 0, NULL, '\0' }
  124. };
  125.  
  126. /* main --- process args, parse program, run it, clean up */
  127.  
  128. int
  129. main(argc, argv)
  130. int argc;
  131. char **argv;
  132. {
  133.     int c;
  134.     char *scan;
  135.     /* the + on the front tells GNU getopt not to rearrange argv */
  136.     const char *optlist = "+F:f:v:W:m:";
  137.     int stopped_early = FALSE;
  138.     int old_optind;
  139.     extern int optind;
  140.     extern int opterr;
  141.     extern char *optarg;
  142.  
  143.     (void) signal(SIGFPE,  (RETSIGTYPE (*) P((int))) catchsig);
  144.     (void) signal(SIGSEGV, (RETSIGTYPE (*) P((int))) catchsig);
  145. #ifdef SIGBUS
  146.     (void) signal(SIGBUS,  (RETSIGTYPE (*) P((int))) catchsig);
  147. #endif
  148.  
  149.     myname = gawk_name(argv[0]);
  150.         argv[0] = (char *) myname;
  151.     os_arg_fixup(&argc, &argv); /* emulate redirection, expand wildcards */
  152.  
  153.     /* remove sccs gunk */
  154.     if (strncmp(version_string, "@(#)", 4) == 0)
  155.         version_string += 4;
  156.  
  157.     if (argc < 2)
  158.         usage(1);
  159.  
  160.     /* initialize the null string */
  161.     Nnull_string = make_string("", 0);
  162.     Nnull_string->numbr = 0.0;
  163.     Nnull_string->type = Node_val;
  164.     Nnull_string->flags = (PERM|STR|STRING|NUM|NUMBER);
  165.  
  166.     /*
  167.      * Tell the regex routines how they should work.
  168.      * Do this before initializing variables, since
  169.      * they could want to do a regexp compile.
  170.      */
  171.     resetup();
  172.  
  173.     /* Set up the special variables */
  174.     /*
  175.      * Note that this must be done BEFORE arg parsing else -F
  176.      * breaks horribly 
  177.      */
  178.     init_vars();
  179.  
  180.     /* worst case */
  181.     emalloc(srcfiles, struct src *, argc * sizeof(struct src), "main");
  182.     memset(srcfiles, '\0', argc * sizeof(struct src));
  183.  
  184.     /* we do error messages ourselves on invalid options */
  185.     opterr = FALSE;
  186.  
  187.     /* option processing. ready, set, go! */
  188.     for (optopt = 0, old_optind = 1;
  189.          (c = getopt_long(argc, argv, optlist, optab, NULL)) != EOF;
  190.          optopt = 0, old_optind = optind) {
  191.         if (do_posix)
  192.             opterr = TRUE;
  193.  
  194.         switch (c) {
  195.         case 'F':
  196.             cmdline_fs(optarg);
  197.             break;
  198.  
  199.         case 'f':
  200.             /*
  201.              * a la MKS awk, allow multiple -f options.
  202.              * this makes function libraries real easy.
  203.              * most of the magic is in the scanner.
  204.              *
  205.              * The following is to allow for whitespace at the end
  206.              * of a #! /bin/gawk line in an executable file
  207.              */
  208.             scan = optarg;
  209.             while (isspace(*scan))
  210.                 scan++;
  211.  
  212.             ++numfiles;
  213.             srcfiles[numfiles].stype = SOURCEFILE;
  214.             if (*scan == '\0')
  215.                 srcfiles[numfiles].val = argv[optind++];
  216.             else
  217.                 srcfiles[numfiles].val = optarg;
  218.             break;
  219.  
  220.         case 'v':
  221.             pre_assign(optarg);
  222.             break;
  223.  
  224.         case 'm':
  225.             /*
  226.              * Research awk extension.
  227.              *    -mf=nnn        set # fields, gawk ignores
  228.              *    -mr=nnn        set record length, ditto
  229.              */
  230.             if (do_lint)
  231.                 warning("-m[fr] option irrelevant in gawk");
  232.             if ((optarg[0] != 'r' && optarg[0] != 'f')
  233.                 || optarg[1] != '=')
  234.                 warning("-m option usage: -m[fr]=nnn");
  235.             break;
  236.  
  237.         case 'W':       /* gawk specific options */
  238.             gawk_option(optarg);
  239.             break;
  240.  
  241.         /* These can only come from long form options */
  242.         case 'C':
  243.             copyleft();
  244.             break;
  245.  
  246.         case 's':
  247.             if (optarg[0] == '\0')
  248.                 warning("empty argument to --source ignored");
  249.             else {
  250.                 srcfiles[++numfiles].stype = CMDLINE;
  251.                 srcfiles[numfiles].val = optarg;
  252.             }
  253.             break;
  254.  
  255.         case 'u':
  256.             usage(0);
  257.             break;
  258.  
  259.         case 'V':
  260.             version();
  261.             break;
  262.  
  263. #ifdef DEBUG
  264.         case 'D':
  265.             yydebug = 2;
  266.             break;
  267. #endif
  268.  
  269.         case 0:
  270.             /*
  271.              * getopt_long found an option that sets a variable
  272.              * instead of returning a letter. Do nothing, just
  273.              * cycle around for the next one.
  274.              */
  275.             break;
  276.  
  277.         case '?':
  278.         default:
  279.             /*
  280.              * New behavior.  If not posix, an unrecognized
  281.              * option stops argument processing so that it can
  282.              * go into ARGV for the awk program to see. This
  283.              * makes use of ``#! /bin/gawk -f'' easier.
  284.              *
  285.              * However, it's never simple. If optopt is set,
  286.              * an option that requires an argument didn't get the
  287.              * argument. We care because if opterr is 0, then
  288.              * getopt_long won't print the error message for us.
  289.              */
  290.             if (! do_posix
  291.                 && (optopt == '\0' || strchr(optlist, optopt) == NULL)) {
  292.                 /*
  293.                  * can't just do optind--. In case of an
  294.                  * option with >= 2 letters, getopt_long
  295.                  * won't have incremented optind.
  296.                  */
  297.                 optind = old_optind;
  298.                 stopped_early = TRUE;
  299.                 goto out;
  300.             } else if (optopt != '\0')
  301.                 /* Use 1003.2 required message format */
  302.                 fprintf(stderr,
  303.                 "%s: option requires an argument -- %c\n",
  304.                     myname, optopt);
  305.             /* else
  306.                 let getopt print error message for us */
  307.             break;
  308.         }
  309.     }
  310. out:
  311.  
  312.     if (do_nostalgia)
  313.         nostalgia();
  314.  
  315.     /* check for POSIXLY_CORRECT environment variable */
  316.     if (! do_posix && getenv("POSIXLY_CORRECT") != NULL) {
  317.         do_posix = TRUE;
  318.         if (do_lint)
  319.             warning(
  320.     "environment variable `POSIXLY_CORRECT' set: turning on --posix");
  321.     }
  322.  
  323.     if (do_posix) {
  324.         if (do_traditional)    /* both on command line */
  325.             warning("--posix overrides --traditional");
  326.         else
  327.             do_traditional = TRUE;
  328.             /*
  329.              * POSIX compliance also implies
  330.              * no GNU extensions either.
  331.              */
  332.     }
  333.  
  334.     /*
  335.      * Tell the regex routines how they should work.
  336.      * Do this again, after argument processing, since do_posix
  337.      * and do_traditional are now paid attention to by resetup().
  338.      */
  339.     if (do_traditional || do_posix) {
  340.         resetup();
  341.  
  342.         /* now handle RS and FS. have to be careful with FS */
  343.         set_RS();
  344.         if (using_fieldwidths()) {
  345.             set_FS();
  346.             set_FIELDWIDTHS();
  347.         } else
  348.             set_FS();
  349.     }
  350.  
  351. #ifdef DEBUG
  352.     setbuf(stdout, (char *) NULL);    /* make debugging easier */
  353. #endif
  354.     if (isatty(fileno(stdout)))
  355.         output_is_tty = TRUE;
  356.     /* No -f or --source options, use next arg */
  357.     if (numfiles == -1) {
  358.         if (optind > argc - 1 || stopped_early) /* no args left or no program */
  359.             usage(1);
  360.         srcfiles[++numfiles].stype = CMDLINE;
  361.         srcfiles[numfiles].val = argv[optind];
  362.         optind++;
  363.     }
  364.  
  365.     init_args(optind, argc, (char *) myname, argv);
  366.     (void) tokexpand();
  367.  
  368.     /* Read in the program */
  369.     if (yyparse() != 0 || errcount != 0)
  370.         exit(1);
  371.     /* recover any space from C based alloca */
  372. #ifdef C_ALLOCA
  373.     (void) alloca(0);
  374. #endif
  375.  
  376.     /* Set up the field variables */
  377.     init_fields();
  378.  
  379.     if (do_lint && begin_block == NULL && expression_value == NULL
  380.          && end_block == NULL)
  381.         warning("no program");
  382.  
  383.     if (begin_block != NULL) {
  384.         in_begin_rule = TRUE;
  385.         (void) interpret(begin_block);
  386.     }
  387.     in_begin_rule = FALSE;
  388.     if (! exiting && (expression_value != NULL || end_block != NULL))
  389.         do_input();
  390.     if (end_block != NULL) {
  391.         in_end_rule = TRUE;
  392.         (void) interpret(end_block);
  393.     }
  394.     in_end_rule = FALSE;
  395.     if (close_io() != 0 && exit_val == 0)
  396.         exit_val = 1;
  397.     exit(exit_val);        /* more portable */
  398.     return exit_val;    /* to suppress warnings */
  399. }
  400.  
  401. /* usage --- print usage information and exit */
  402.  
  403. static void
  404. usage(exitval)
  405. int exitval;
  406. {
  407.     char *opt1 = " -f progfile [--]";
  408.     char *regops = " [POSIX or GNU style options]";
  409.  
  410.     fprintf(stderr, "Usage: %s%s%s file ...\n\t%s%s [--] %cprogram%c file ...\n",
  411.         myname, regops, opt1, myname, regops, quote, quote);
  412.  
  413.     /* GNU long options info. Gack. */
  414.     fputs("POSIX options:\t\tGNU long options:\n", stderr);
  415.     fputs("\t-f progfile\t\t--file=progfile\n", stderr);
  416.     fputs("\t-F fs\t\t\t--field-separator=fs\n", stderr);
  417.     fputs("\t-v var=val\t\t--assign=var=val\n", stderr);
  418.     fputs("\t-m[fr]=val\n", stderr);
  419.     fputs("\t-W compat\t\t--compat\n", stderr);
  420.     fputs("\t-W copyleft\t\t--copyleft\n", stderr);
  421.     fputs("\t-W copyright\t\t--copyright\n", stderr);
  422.     fputs("\t-W help\t\t\t--help\n", stderr);
  423.     fputs("\t-W lint\t\t\t--lint\n", stderr);
  424.     fputs("\t-W lint-old\t\t--lint-old\n", stderr);
  425. #ifdef NOSTALGIA
  426.     fputs("\t-W nostalgia\t\t--nostalgia\n", stderr);
  427. #endif
  428. #ifdef DEBUG
  429.     fputs("\t-W parsedebug\t\t--parsedebug\n", stderr);
  430. #endif
  431.     fputs("\t-W posix\t\t--posix\n", stderr);
  432.     fputs("\t-W re-interval\t\t--re-interval\n", stderr);
  433.     fputs("\t-W source=program-text\t--source=program-text\n", stderr);
  434.     fputs("\t-W traditional\t\t--traditional\n", stderr);
  435.     fputs("\t-W usage\t\t--usage\n", stderr);
  436.     fputs("\t-W version\t\t--version\n", stderr);
  437.     exit(exitval);
  438. }
  439.  
  440. /* copyleft --- print out the short GNU copyright information */
  441.  
  442. static void
  443. copyleft()
  444. {
  445.     static char blurb_part1[] =
  446. "Copyright (C) 1989, 1991-1995 Free Software Foundation.\n\
  447. \n\
  448. This program is free software; you can redistribute it and/or modify\n\
  449. it under the terms of the GNU General Public License as published by\n\
  450. the Free Software Foundation; either version 2 of the License, or\n\
  451. (at your option) any later version.\n\
  452. \n";
  453.     static char blurb_part2[] =
  454. "This program is distributed in the hope that it will be useful,\n\
  455. but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
  456. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\
  457. GNU General Public License for more details.\n\
  458. \n";
  459.     static char blurb_part3[] =
  460. "You should have received a copy of the GNU General Public License\n\
  461. along with this program; if not, write to the Free Software\n\
  462. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n";
  463.  
  464.     /* multiple blurbs are needed for some brain dead compilers. */
  465.     fputs(blurb_part1, stderr);
  466.     fputs(blurb_part2, stderr);
  467.     fputs(blurb_part3, stderr);
  468.     fflush(stderr);
  469. }
  470.  
  471. /* cmdline_fs --- set FS from the command line */
  472.  
  473. static void
  474. cmdline_fs(str)
  475. char *str;
  476. {
  477.     register NODE **tmp;
  478.  
  479.     tmp = get_lhs(FS_node, (Func_ptr *) 0);
  480.     unref(*tmp);
  481.     /*
  482.      * Only if in full compatibility mode check for the stupid special
  483.      * case so -F\t works as documented in awk book even though the shell
  484.      * hands us -Ft.  Bleah!
  485.      *
  486.      * Thankfully, Posix didn't propogate this "feature".
  487.      */
  488.     if (str[0] == 't' && str[1] == '\0') {
  489.         if (do_lint)
  490.             warning("-Ft does not set FS to tab in POSIX awk");
  491.         if (do_traditional && ! do_posix)
  492.             str[0] = '\t';
  493.     }
  494.     *tmp = make_str_node(str, strlen(str), SCAN); /* do process escapes */
  495.     set_FS();
  496. }
  497.  
  498. /* init_args --- set up ARGV from stuff on the command line */
  499.  
  500. static void
  501. init_args(argc0, argc, argv0, argv)
  502. int argc0, argc;
  503. char *argv0;
  504. char **argv;
  505. {
  506.     int i, j;
  507.     NODE **aptr;
  508.  
  509.     ARGV_node = install("ARGV", node(Nnull_string, Node_var_array, (NODE *) NULL));
  510.     aptr = assoc_lookup(ARGV_node, tmp_number(0.0));
  511.     *aptr = make_string(argv0, strlen(argv0));
  512.     (*aptr)->flags |= MAYBE_NUM;
  513.     for (i = argc0, j = 1; i < argc; i++) {
  514.         aptr = assoc_lookup(ARGV_node, tmp_number((AWKNUM) j));
  515.         *aptr = make_string(argv[i], strlen(argv[i]));
  516.         (*aptr)->flags |= MAYBE_NUM;
  517.         j++;
  518.     }
  519.     ARGC_node = install("ARGC",
  520.             node(make_number((AWKNUM) j), Node_var, (NODE *) NULL));
  521. }
  522.  
  523. /*
  524.  * Set all the special variables to their initial values.
  525.  * Note that some of the variables that have set_FOO routines should
  526.  * *N*O*T* have those routines called upon initialization, and thus
  527.  * they have NULL entries in that field. This is notably true of FS
  528.  * and IGNORECASE.
  529.  */
  530. struct varinit {
  531.     NODE **spec;
  532.     const char *name;
  533.     NODETYPE type;
  534.     const char *strval;
  535.     AWKNUM numval;
  536.     Func_ptr assign;
  537. };
  538. static struct varinit varinit[] = {
  539. {&CONVFMT_node,    "CONVFMT",    Node_CONVFMT,        "%.6g",    0,  set_CONVFMT },
  540. {&NF_node,    "NF",        Node_NF,        NULL,    -1, set_NF },
  541. {&FIELDWIDTHS_node, "FIELDWIDTHS", Node_FIELDWIDTHS,    "",    0,  NULL },
  542. {&NR_node,    "NR",        Node_NR,        NULL,    0,  set_NR },
  543. {&FNR_node,    "FNR",        Node_FNR,        NULL,    0,  set_FNR },
  544. {&FS_node,    "FS",        Node_FS,        " ",    0,  NULL },
  545. {&RS_node,    "RS",        Node_RS,        "\n",    0,  set_RS },
  546. {&IGNORECASE_node, "IGNORECASE", Node_IGNORECASE,    NULL,    0,  NULL },
  547. {&FILENAME_node, "FILENAME",    Node_var,        "",    0,  NULL },
  548. {&OFS_node,    "OFS",        Node_OFS,        " ",    0,  set_OFS },
  549. {&ORS_node,    "ORS",        Node_ORS,        "\n",    0,  set_ORS },
  550. {&OFMT_node,    "OFMT",        Node_OFMT,        "%.6g",    0,  set_OFMT },
  551. {&RLENGTH_node, "RLENGTH",    Node_var,        NULL,    0,  NULL },
  552. {&RSTART_node,    "RSTART",    Node_var,        NULL,    0,  NULL },
  553. {&SUBSEP_node,    "SUBSEP",    Node_var,        "\034",    0,  NULL },
  554. {&ARGIND_node,    "ARGIND",    Node_var,        NULL,    0,  NULL },
  555. {&ERRNO_node,    "ERRNO",    Node_var,        NULL,    0,  NULL },
  556. {&RT_node,    "RT",        Node_var,        "",    0,  NULL },
  557. {0,        NULL,        Node_illegal,        NULL,    0,  NULL },
  558. };
  559.  
  560. /* init_vars --- actually initialize everything in the symbol table */
  561.  
  562. static void
  563. init_vars()
  564. {
  565.     register struct varinit *vp;
  566.  
  567.     for (vp = varinit; vp->name; vp++) {
  568.         *(vp->spec) = install((char *) vp->name,
  569.           node(vp->strval == NULL ? make_number(vp->numval)
  570.                 : make_string((char *) vp->strval,
  571.                     strlen(vp->strval)),
  572.                vp->type, (NODE *) NULL));
  573.         (*(vp->spec))->flags |= SCALAR;
  574.         if (vp->assign)
  575.             (*(vp->assign))();
  576.     }
  577. }
  578.  
  579. /* load_environ --- populate the ENVIRON array */
  580.  
  581. void
  582. load_environ()
  583. {
  584. #if ! (defined(MSDOS) && !defined(DJGPP)) && ! defined(OS2) && ! (defined(VMS) && defined(__DECC))
  585.     extern char **environ;
  586. #endif
  587.     register char *var, *val, *cp;
  588.     NODE **aptr;
  589.     register int i;
  590.  
  591.     ENVIRON_node = install("ENVIRON", 
  592.             node(Nnull_string, Node_var, (NODE *) NULL));
  593.     for (i = 0; environ[i] != NULL; i++) {
  594.         static char nullstr[] = "";
  595.  
  596.         var = environ[i];
  597.         val = strchr(var, '=');
  598.         if (val != NULL)
  599.             *val++ = '\0';
  600.         else
  601.             val = nullstr;
  602.         aptr = assoc_lookup(ENVIRON_node, tmp_string(var, strlen(var)));
  603.         *aptr = make_string(val, strlen(val));
  604.         (*aptr)->flags |= (MAYBE_NUM|SCALAR);
  605.  
  606.         /* restore '=' so that system() gets a valid environment */
  607.         if (val != nullstr)
  608.             *--val = '=';
  609.     }
  610.     /*
  611.      * Put AWKPATH into ENVIRON if it's not there.
  612.      * This allows querying it from outside gawk.
  613.      */
  614.     if ((cp = getenv("AWKPATH")) == NULL) {
  615.         aptr = assoc_lookup(ENVIRON_node, tmp_string("AWKPATH", 7));
  616.         *aptr = make_string(defpath, strlen(defpath));
  617.         (*aptr)->flags |= SCALAR;
  618.     }
  619. }
  620.  
  621. /* arg_assign --- process a command-line assignment */
  622.  
  623. char *
  624. arg_assign(arg)
  625. char *arg;
  626. {
  627.     char *cp, *cp2;
  628.     int badvar;
  629.     Func_ptr after_assign = NULL;
  630.     NODE *var;
  631.     NODE *it;
  632.     NODE **lhs;
  633.  
  634.     cp = strchr(arg, '=');
  635.     if (cp != NULL) {
  636.         *cp++ = '\0';
  637.         /* first check that the variable name has valid syntax */
  638.         badvar = FALSE;
  639.         if (! isalpha(arg[0]) && arg[0] != '_')
  640.             badvar = TRUE;
  641.         else
  642.             for (cp2 = arg+1; *cp2; cp2++)
  643.                 if (! isalnum(*cp2) && *cp2 != '_') {
  644.                     badvar = TRUE;
  645.                     break;
  646.                 }
  647.         if (badvar)
  648.             fatal("illegal name `%s' in variable assignment", arg);
  649.  
  650.         /*
  651.          * Recent versions of nawk expand escapes inside assignments.
  652.          * This makes sense, so we do it too.
  653.          */
  654.         it = make_str_node(cp, strlen(cp), SCAN);
  655.         it->flags |= (MAYBE_NUM|SCALAR);
  656.         var = variable(arg, FALSE, Node_var);
  657.         lhs = get_lhs(var, &after_assign);
  658.         unref(*lhs);
  659.         *lhs = it;
  660.         if (after_assign != NULL)
  661.             (*after_assign)();
  662.         *--cp = '=';    /* restore original text of ARGV */
  663.     }
  664.     return cp;
  665. }
  666.  
  667. /* pre_assign --- handle -v, print a message and die if a problem */
  668.  
  669. static void
  670. pre_assign(v)
  671. char *v;
  672. {
  673.     if (arg_assign(v) == NULL) {
  674.         fprintf(stderr,
  675.             "%s: `%s' argument to `-v' not in `var=value' form\n",
  676.                 myname, v);
  677.         usage(1);
  678.     }
  679. }
  680.  
  681. /* catchsig --- catch signals */
  682.  
  683. RETSIGTYPE
  684. catchsig(sig, code)
  685. int sig, code;
  686. {
  687. #ifdef lint
  688.     code = 0; sig = code; code = sig;
  689. #endif
  690.     if (sig == SIGFPE) {
  691.         fatal("floating point exception");
  692.     } else if (sig == SIGSEGV
  693. #ifdef SIGBUS
  694.             || sig == SIGBUS
  695. #endif
  696.     ) {
  697.         msg("fatal error: internal error");
  698.         /* fatal won't abort() if not compiled for debugging */
  699.         abort();
  700.     } else
  701.         cant_happen();
  702.     /* NOTREACHED */
  703. }
  704.  
  705. /* gawk_option --- do gawk specific things */
  706.  
  707. static void
  708. gawk_option(optstr)
  709. char *optstr;
  710. {
  711.     char *cp;
  712.  
  713.     for (cp = optstr; *cp != '\0'; cp++) {
  714.         /* keep this switch sorted as optioins are added */
  715.         switch (*cp) {
  716.         case ' ':
  717.         case '\t':
  718.         case ',':
  719.             break;
  720.         case 'c':
  721.         case 'C':
  722.             if (strncasecmp(cp, "copyright", 9) == 0) {
  723.                 cp += 8;
  724.                 copyleft();
  725.             } else if (strncasecmp(cp, "copyleft", 8) == 0) {
  726.                 cp += 7;
  727.                 copyleft();
  728.             } else if (strncasecmp(cp, "compat", 6) == 0) {
  729.                 cp += 5;
  730.                 do_traditional = TRUE;
  731.             } else
  732.                 goto unknown;
  733.             break;
  734.         case 'H':
  735.         case 'h':
  736.             if (strncasecmp(cp, "help", 4) != 0)
  737.                 goto unknown;
  738.             cp += 3;
  739.             usage(0);
  740.             break;
  741.         case 'l':
  742.         case 'L':
  743.             if (strncasecmp(cp, "lint-old", 8) == 0) {
  744.                 cp += 7;
  745.                 do_lint_old = TRUE;
  746.             } else if (strncasecmp(cp, "lint", 4) == 0) {
  747.                 cp += 3;
  748.                 do_lint = TRUE;
  749.             } else
  750.                 goto unknown;
  751.             break;
  752.         case 'n':
  753.         case 'N':
  754.             /*
  755.              * Undocumented feature,
  756.              * inspired by nostalgia, and a T-shirt
  757.              */
  758.             if (strncasecmp(cp, "nostalgia", 9) != 0)
  759.                 goto unknown;
  760.             nostalgia();
  761.             break;
  762.         case 'p':
  763.         case 'P':
  764. #ifdef DEBUG
  765.             if (strncasecmp(cp, "parsedebug", 10) == 0) {
  766.                 cp += 9;
  767.                 yydebug = 2;
  768.                 break;
  769.             }
  770. #endif
  771.             if (strncasecmp(cp, "posix", 5) != 0)
  772.                 goto unknown;
  773.             cp += 4;
  774.             do_posix = do_traditional = TRUE;
  775.             break;
  776.         case 'r':
  777.         case 'R':
  778.             if (strncasecmp(cp, "re-interval", 11) != 0)
  779.                 goto unknown;
  780.             do_intervals = TRUE;
  781.             break;
  782.         case 's':
  783.         case 'S':
  784.             if (strncasecmp(cp, "source=", 7) != 0)
  785.                 goto unknown;
  786.             cp += 7;
  787.             if (cp[0] == '\0')
  788.                 warning("empty argument to -Wsource ignored");
  789.             else {
  790.                 srcfiles[++numfiles].stype = CMDLINE;
  791.                 srcfiles[numfiles].val = cp;
  792.                 return;
  793.             }
  794.             break;
  795.         case 't':
  796.         case 'T':
  797.             if (strncasecmp(cp, "traditional", 11) != 0)
  798.                 goto unknown;
  799.             do_traditional = TRUE;
  800.             cp += 11;
  801.             break;
  802.         case 'U':
  803.         case 'u':
  804.             if (strncasecmp(cp, "usage", 5) != 0)
  805.                 goto unknown;
  806.             cp += 4;
  807.             usage(0);
  808.             break;
  809.         case 'v':
  810.         case 'V':
  811.             /* print version */
  812.             if (strncasecmp(cp, "version", 7) != 0)
  813.                 goto unknown;
  814.             else
  815.                 cp += 6;
  816.             version();
  817.             break;
  818.         default:
  819.         unknown:
  820.             fprintf(stderr, "'%c' -- unknown option, ignored\n",
  821.                 *cp);
  822.             break;
  823.         }
  824.     }
  825. }
  826.  
  827. /* nostalgia --- print the famous error message and die */
  828.  
  829. static void
  830. nostalgia()
  831. {
  832.     fprintf(stderr, "awk: bailing out near line 1\n");
  833.     abort();
  834. }
  835.  
  836. /* version --- print version message */
  837.  
  838. static void
  839. version()
  840. {
  841.     fprintf(stderr, "%s, patchlevel %d\n", version_string, PATCHLEVEL);
  842.     /* per GNU coding standards, exit successfully, do nothing else */
  843.     exit(0);
  844. }
  845.